package com.softserveinc.moviedbapp.provider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;

import com.softserveinc.moviedbapp.cache.MovieDBOpenHelper;
import com.softserveinc.moviedbapp.cache.tables.MovieDetailsTable;
import com.softserveinc.moviedbapp.cache.tables.MovieDetailsTable.MovieDetailsColumns;
import com.softserveinc.moviedbapp.cache.tables.MovieImagesTable;
import com.softserveinc.moviedbapp.cache.tables.MovieImagesTable.MovieImageColumns;
import com.softserveinc.moviedbapp.cache.tables.MovieTable.MovieColumns;
import com.softserveinc.moviedbapp.connection.GeneralException;
import com.softserveinc.moviedbapp.manager.DownloadManager;
import com.softserveinc.moviedbapp.model.MovieDetails;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class MovieDetailsProvider extends ContentProvider {

	public static final String TAG = MovieDetailsProvider.class.getSimpleName();

	public static final String AUTHORITY = "com.softserveinc.moviedbapp.moviedetailsprovider";
	public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
	public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "movie");
	public static final String MOVIE_DETAILS_MIME_TYPE = "vnd.android.cursor.item/vnd.softserveinc.movie";

	public static final String SELECT_MOVIE_DETAILS_QUERY = "SELECT " + MovieDetailsTable.TABLE_NAME + ".* FROM "
			+ MovieDetailsTable.TABLE_NAME + " WHERE " + MovieDetailsTable.TABLE_NAME + "." + MovieColumns.MOVIE_ID
			+ " = ?";

	public static final String SELECT_MOVIE_IMAGE_URLS_QUERY = "SELECT " + MovieImageColumns.IMAGE_URL + ", "
			+ MovieImageColumns.IMAGE_SIZE + " FROM " + MovieImagesTable.TABLE_NAME + " WHERE " + MovieColumns.MOVIE_ID
			+ " = ? ORDER BY " + MovieImageColumns.IMAGE_URL + " ASC";

	public static final String SELECT_MOVIE_DETAILS_LAST_MODIFIED_AT_QUERY = "SELECT "
			+ MovieDetailsColumns.MOVIE_INFO_LAST_MODIFIED_AT + " FROM " + MovieDetailsTable.TABLE_NAME + " WHERE "
			+ MovieColumns.MOVIE_ID + " = ?";

	private final static int MOVIE = 1;
	private final static int MOVIE_ID = 2;
	private final static int MOVIE_IMAGES = 3;

	private MovieDBOpenHelper mDbHelper;
	private SQLiteDatabase mDataBase;
	private DownloadManager mDownloadManager;

	private final static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

	static {
		sUriMatcher.addURI(AUTHORITY, "movie/backdrops/#", MOVIE_IMAGES);
		sUriMatcher.addURI(AUTHORITY, "movie", MOVIE);
		sUriMatcher.addURI(AUTHORITY, "movie/#", MOVIE_ID);
	}

	static final HashMap<String, String> projectionMap = new HashMap<String, String>() {

		private static final long serialVersionUID = 1L;
		{
			put(BaseColumns._ID, BaseColumns._ID);
			put(MovieColumns.MOVIE_ID, MovieColumns.MOVIE_ID);
			put(MovieDetailsColumns.MOVIE_INFO_OVERVIEW, MovieDetailsColumns.MOVIE_INFO_OVERVIEW);
			put(MovieDetailsColumns.MOVIE_INFO_CAST, MovieDetailsColumns.MOVIE_INFO_CAST);
			put(MovieDetailsColumns.MOVIE_INFO_TRAILER, MovieDetailsColumns.MOVIE_INFO_TRAILER);
		}
	};

	private long mLastRefreshedAt = System.currentTimeMillis();
	private boolean mFirstQuery = true;
	private static final long DETAILS_UPDATE_PERIOD = 18000000L/* 86400000L */;

	@Override
	public boolean onCreate() {
		Log.d(TAG, "onCreate() ");
		mDbHelper = new MovieDBOpenHelper(getContext());
		mDownloadManager = new DownloadManager();
		mDataBase = mDbHelper.getWritableDatabase();
		return true;
	}

	@Override
	public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs,
			final String sortOrder) {
		Log.d(TAG, "inside query()");
		/*
		 * check whether or not the projection passed in has no invalid column
		 * names
		 */
		// Set<String> projectionCols = new HashSet<String>();
		if (projection != null) {
			Set<String> projectionCols = new HashSet<String>(Arrays.asList(projection));
			if (!projectionMap.keySet().containsAll(projectionCols)) {
				throw new IllegalArgumentException("Unrecognized column(s) in projection");
			}
		}

		String selectionArg1;
		String selectionArg2;
		int movieId;

		switch (sUriMatcher.match(uri)) {
		case MOVIE:
			selectionArg1 = selectionArgs[0];
			selectionArg2 = selectionArgs[1];
			Log.d(TAG, "URI = " + uri.toString());
			movieId = Integer.valueOf(selectionArgs[2]);

			return null;
			// return getMovieCursor(listTypeInt, language, parserType,
			// listType, results);

		case MOVIE_ID:
			selectionArg1 = selectionArgs[0];
			selectionArg2 = selectionArgs[1];
			Log.d(TAG, "URI = " + uri.toString());
			movieId = (int) ContentUris.parseId(uri);

			try {
				return getMovieDetailsCursor(movieId, selectionArg1, selectionArg2);
			} catch (GeneralException e) {
				Log.e(TAG, "No connection apparently", e);
				return null;
			}

		case MOVIE_IMAGES:
			Log.d(TAG, "URI = " + uri.toString());
			movieId = (int) ContentUris.parseId(uri);

			return getMovieImagesCursor(movieId);

		case UriMatcher.NO_MATCH:
		default:
			throw new IllegalArgumentException("unrecognized URI " + uri);
		}
	}

	@Override
	public int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) {
		return 0;
	}

	@Override
	public Uri insert(final Uri uri, final ContentValues values) {
		return null;
	}

	@Override
	public int delete(final Uri uri, final String selection, final String[] selectionArgs) {
		return 0;
	}

	@Override
	public String getType(final Uri paramUri) {
		return null;
	}

	private Cursor getMovieDetailsCursor(final int movieId, final String language, final String parserType)
			throws GeneralException {
		Log.d(TAG, "gonna get movie details");
		Log.d(TAG, "running " + SELECT_MOVIE_DETAILS_QUERY + movieId);
		Cursor cursor = mDataBase.rawQuery(SELECT_MOVIE_DETAILS_QUERY, new String[] { String.valueOf(movieId) });
		if (cursor.moveToFirst()) {
			Log.d(TAG, "movie details exist");
			long now = System.currentTimeMillis();
			/* if 24 hours haven't passed yet - don't update the list */
			Log.d(TAG, "From last update " + String.format("%.2f", (now - mLastRefreshedAt) / 1000.0 / 60.0)
					+ " mins passed");
			if (!mFirstQuery && now - mLastRefreshedAt < DETAILS_UPDATE_PERIOD) {
				return cursor;
			}
		}
		Log.d(TAG, "need to download/update movie details");
		MovieDetails movieInfo = mDownloadManager.getMovieInfo(language, parserType, movieId);

		insertMovieInfo(movieInfo);

		/* re-query and return the cursor */
		cursor.close();
		Log.d(TAG, "running " + SELECT_MOVIE_DETAILS_QUERY + movieId);
		return mDataBase.rawQuery(SELECT_MOVIE_DETAILS_QUERY, new String[] { String.valueOf(movieId) });
	}

	private Cursor getMovieImagesCursor(final int movieId) {
		Log.d(TAG, "gonna get movie image urls");
		Log.d(TAG, "running " + SELECT_MOVIE_IMAGE_URLS_QUERY + movieId);
		return mDataBase.rawQuery(SELECT_MOVIE_IMAGE_URLS_QUERY, new String[] { String.valueOf(movieId) });
	}

	/* insert a single row into movie_details */
	private void insertMovieInfo(final MovieDetails movieInfo) {
		ContentValues values = new ContentValues();
		if (!movieDetailsExist(movieInfo)) {
			values.put(MovieColumns.MOVIE_ID, movieInfo.getMovieId());
			values.put(MovieDetailsColumns.MOVIE_INFO_LAST_MODIFIED_AT, movieInfo.getLastModifiedAt());
			values.put(MovieDetailsColumns.MOVIE_INFO_OVERVIEW, movieInfo.getOverview());
			values.put(MovieDetailsColumns.MOVIE_INFO_TRAILER, movieInfo.getTrailerUrl());
			values.put(MovieDetailsColumns.MOVIE_INFO_CAST, movieInfo.getCast());
			Log.d(TAG, "inserting into \"movie_details\"");
			mDataBase.insert(MovieDetailsTable.TABLE_NAME, null, values);

			insertMovieImageUrls(movieInfo.getMovieId(), movieInfo.getImagesUrls());
		}
	}

	private void insertMovieImageUrls(final int movieId, final Map<String, List<String>> imagesUrls) {
		ContentValues values = new ContentValues();
		Iterator<String> iterator = imagesUrls.keySet().iterator();

		while (iterator.hasNext()) {
			String url = iterator.next();
			Log.d(TAG, url);
			List<String> typeSize = imagesUrls.get(url);
			values.put(MovieColumns.MOVIE_ID, movieId);
			values.put(MovieImageColumns.IMAGE_TYPE, typeSize.get(0));
			values.put(MovieImageColumns.IMAGE_SIZE, typeSize.get(1));
			values.put(MovieImageColumns.IMAGE_URL, url);
			Log.d(TAG, "inserting into \"movie_images\"");
			mDataBase.insert(MovieImagesTable.TABLE_NAME, null, values);
		}

	}

	private boolean movieDetailsExist(final MovieDetails movieInfo) {
		Cursor cursor = mDataBase.rawQuery(SELECT_MOVIE_DETAILS_LAST_MODIFIED_AT_QUERY,
				new String[] { String.valueOf(movieInfo.getMovieId()) });
		Log.d(TAG, "checking if movie details exists and is up to date");
		if (cursor.moveToFirst()) {
			Log.d(TAG, "movie details exist");
			String lastModifiedAt = cursor.getString(cursor
					.getColumnIndex(MovieDetailsColumns.MOVIE_INFO_LAST_MODIFIED_AT));
			cursor.close();

			if (lastModifiedAt.equals(movieInfo.getLastModifiedAt())) {
				Log.d(TAG, "dates are the same");
				return true;
			} else {
				Log.d(TAG, "dates are not the same so i'm gonna update");
				updateMovieDetails(movieInfo);
				updateMovieImageUrls(movieInfo.getMovieId(), movieInfo.getImagesUrls());
				return true;
			}

		}
		Log.d(TAG, "movie doesn't even exist");
		cursor.close();
		return false;
	}

	private long updateMovieDetails(final MovieDetails movieInfo) {
		/* update movie_details */
		ContentValues values = new ContentValues();
		values.put(MovieDetailsColumns.MOVIE_INFO_LAST_MODIFIED_AT, movieInfo.getLastModifiedAt());
		values.put(MovieDetailsColumns.MOVIE_INFO_OVERVIEW, movieInfo.getOverview());
		values.put(MovieDetailsColumns.MOVIE_INFO_TRAILER, movieInfo.getTrailerUrl());
		values.put(MovieDetailsColumns.MOVIE_INFO_CAST, movieInfo.getCast());

		Log.d(TAG, "updating \"movie_details\" for movieId " + movieInfo.getMovieId());
		return mDataBase.update(MovieDetailsTable.TABLE_NAME, values,
				MovieColumns.MOVIE_ID + " = " + movieInfo.getMovieId(), null);
	}

	private void updateMovieImageUrls(final int movieId, final Map<String, List<String>> imagesUrls) {
		ContentValues values = new ContentValues();
		Iterator<String> iterator = imagesUrls.keySet().iterator();

		while (iterator.hasNext()) {
			String url = iterator.next();
			List<String> typeSize = imagesUrls.get(url);
			values.put(MovieImageColumns.IMAGE_TYPE, typeSize.get(0));
			values.put(MovieImageColumns.IMAGE_SIZE, typeSize.get(1));
			values.put(MovieImageColumns.IMAGE_URL, url);

			Log.d(TAG, "updating \"movie_images\" for movieId " + movieId);
			mDataBase.update(MovieImagesTable.TABLE_NAME, values, MovieColumns.MOVIE_ID + " = " + movieId, null);
			values.clear();
		}
	}
}
